/**
 * Created on 2007-03-06 22:02
 */
package main;

import game.frames.*;
import game.BlockManager;
import game.Settings;
import game.ShapeManager;
import game.input.InputManager;

import java.applet.Applet;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JPanel;

import game.score.ScoreManager;

import game.media.MediaManager;
import game.media.gfx.EntityManager;
import game.media.gfx.RenderManager;

/**
 * @author David Wallin
 * 
 * Main Applet class. Looks best in dimensions 400 x 500
 */
public class Main extends Applet implements FrameSwitchListener//, MouseListener
{
	/**
	 * Generated serial version unique id
	 */
	private static final long		serialVersionUID = -9093460366336157543L;
	
	private JPanel						m_Panel;
	private ArrayList<BasicFrame>		m_Frames;
	private ControlChangeFrame			m_ControlChangeFrame;
	private GameFrame					m_GameFrame;
	private HighscoreFrame				m_HighscoreFrame;
	private MainMenuFrame				m_MainMenuFrame;
	private NameEntryFrame				m_NameEntryFrame;
	private OptionsFrame				m_OptionsFrame;
	private CreditsFrame				m_CreditsFrame;
	private InstructionsFrame			m_InstructionsFrame;
	private DifficultyFrame				m_DifficultyFrame;
	
	private RenderManager				m_RenderManager = RenderManager.INSTANCE;
	private EntityManager				m_EntityManager = EntityManager.INSTANCE;
	private ScoreManager				m_ScoreManager = ScoreManager.INSTANCE;
	private Settings					m_Settings = Settings.INSTANCE;
	
	private BlockManager				m_BlockManager;
	
	private Loop						m_Loop;
	
	public void init()
	{
		//initialize the applet
		System.out.println(">> INIT <<");
		
		/*
		 * The frames
		 */
		m_Panel = new JPanel();
		m_Frames = new ArrayList<BasicFrame>();
		
		m_ControlChangeFrame = new ControlChangeFrame();
		m_GameFrame = new GameFrame(this.getWidth(), this.getHeight());
		m_HighscoreFrame = new HighscoreFrame();
		m_MainMenuFrame = new MainMenuFrame();
		m_NameEntryFrame = new NameEntryFrame();
		m_OptionsFrame = new OptionsFrame();
		m_CreditsFrame = new CreditsFrame();
		m_InstructionsFrame = new InstructionsFrame();
		m_DifficultyFrame = new DifficultyFrame();
		
		m_ControlChangeFrame.addFrameSwitchListener(this);
		m_GameFrame.addFrameSwitchListener(this);
		m_HighscoreFrame.addFrameSwitchListener(this);
		m_MainMenuFrame.addFrameSwitchListener(this);
		m_NameEntryFrame.addFrameSwitchListener(this);
		m_OptionsFrame.addFrameSwitchListener(this);
		m_CreditsFrame.addFrameSwitchListener(this);
		m_InstructionsFrame.addFrameSwitchListener(this);
		m_DifficultyFrame.addFrameSwitchListener(this);
		
		m_Frames.add(m_ControlChangeFrame);
		m_Frames.add(m_GameFrame);
		m_Frames.add(m_HighscoreFrame);
		m_Frames.add(m_MainMenuFrame);
		m_Frames.add(m_NameEntryFrame);
		m_Frames.add(m_OptionsFrame);
		m_Frames.add(m_CreditsFrame);
		m_Frames.add(m_InstructionsFrame);
		m_Frames.add(m_DifficultyFrame);
		
		m_OptionsFrame.setOptionsCheckers(); //this is needed in case the player starts with a "hard" game
		
		/*
		 * Graphics stuff
		 */
		m_Panel.setSize(this.getSize());
		this.add(m_Panel);
		this.setBackground(Color.black);
		m_Panel.setBackground(Color.black);
		
		m_RenderManager.setGraphicsContext(this);
		m_RenderManager.initialize();
		m_EntityManager.initialize();
		ShapeManager.INSTANCE.initialize();
		m_ScoreManager.initialize();
		
		/*
		 * Block manager
		 */
		m_BlockManager = new BlockManager();
		m_BlockManager.initialize();
		
		/*
		 * Keyboard input
		 */
		addKeyListener(InputManager.INSTANCE);
		InputManager.INSTANCE.setMainWindow(this);
		
		/*
		 * Media path
		 */
		try
		{
			String mediaPath = this.getParameter("MEDIA_PATH");
			
			if(!mediaPath.equals("null"))
			{
				MediaManager.INSTANCE.setMediaPath(mediaPath);
			}
		}
		catch(NullPointerException e)
		{
			System.out.println("no media path, falling back to default");
			//no alternate media path given, swallow exception
		}
		
		/*
		 * Media preloader
		 */
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_DROP) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_GAME_OVER) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_LAND) == null)
		{
			System.out.println("fatal error! media preloader sound land");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_LEVELUP) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_MOVE) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_REMOVELINE) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_REMOVELINE_SPECIAL) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
		
		if(MediaManager.INSTANCE.getAudioClip(MediaManager.SOUND_ROTATE) == null)
		{
			System.out.println("fatal error! media preloader sound gameover");
		}
	}
	
	public void start()
	{
		//start the applet
		System.out.println(">> START <<");
		//first active frame
		this.setDisplayedFrame(m_MainMenuFrame);
	}
	
	public void stop()
	{
		//stop the applet
		if(m_Loop != null)
		{
			m_Loop.setOwner(null); //this is needed, else the m_Loop.run will think it's supposed to switch the displayed frame to the "enter your name"-frame
			m_Loop.quit();
			
			while(!m_Loop.getFinished())
			{
				try
				{
					Thread.sleep(100);
				}
				catch(InterruptedException e)
				{
					System.out.println("interrupted during sleep while waiting for m_Loop.run to finish in Applet.stop");
					e.printStackTrace();
				}
			}
		}
	}
	
	public void destroy()
	{
		//exit the applet
		m_RenderManager.tearDown();
		m_EntityManager.tearDown();
	}
	
	public void pressQuit()
	{
		m_Loop.quit();
		m_Settings.setReversedFlip(m_OptionsFrame.getReverseFlipState());
	}
	
	public void pressPause()
	{
		m_Loop.pressPause();
	}
	
	private class Loop extends Thread
	{
		private RenderManager			m_InnerRenderManager = RenderManager.INSTANCE;
		private EntityManager			m_InnerEntityManager = EntityManager.INSTANCE;
		private ScoreManager			m_InnerScoreManager = ScoreManager.INSTANCE;
		private InputManager			m_InputManager = InputManager.INSTANCE;
		private BlockManager			m_InnerBlockManager;
		private FrameSwitchListener		m_Owner;
		
		private boolean					m_Running;
		private boolean					m_Finished;
		private boolean					m_Paused;
		
		public Loop(BlockManager blockManager)
		{
			m_InnerBlockManager = blockManager;
		}
		
		public void run()
		{
			m_Running = true;
			m_Finished = false;
			m_Paused = true;
			
			long lastTime = System.currentTimeMillis();
			long currentTime = 0;
			int deltaTime = 0;
			
			while(m_Running)
			{
				currentTime = System.currentTimeMillis();
				
				if(m_Paused)
				{
					deltaTime = 0;
				}
				else
				{
					deltaTime = (int)(currentTime - lastTime);
				}
				
				lastTime = currentTime;
				
				m_InnerRenderManager.cls();
				m_InnerScoreManager.update(deltaTime);
				m_InnerBlockManager.update(deltaTime);
				m_InnerEntityManager.update(deltaTime);
				m_InnerRenderManager.update(deltaTime);
				
				try
				{
					Thread.sleep(10);
				}
				catch(InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			
			if(m_Owner != null)
			{
				m_InnerRenderManager.cls();
				m_InnerRenderManager.update(deltaTime);
				
				if((m_ScoreManager.getRealScore() > 0) && !m_InputManager.getGameEndedThroughQuitPressed())
				{
					m_Owner.switchCalled(Frames.ENTER_NAME);
				}
				else
				{
					m_Owner.switchCalled(Frames.HIGHSCORES);
				}
			}
			
			m_Finished = true;
		}
		
		public void pressPause()
		{
			m_Paused = !m_Paused;
			m_InnerBlockManager.pressPause(m_Paused);
		}
		
		public void quit()
		{
			m_Running = false;
		}
		
		public void setOwner(FrameSwitchListener l)
		{
			m_Owner = l;
		}
		
		public boolean getFinished()
		{
			return m_Finished;
		}
	}
	
	/* (non-Javadoc)
	 * @see frames.FrameSwitchListener#switchCalled(frames.Frames)
	 */
	public void switchCalled(Frames frame)
	{
		if(frame == Frames.CONTROLS)
		{
			this.setDisplayedFrame(m_ControlChangeFrame);
		}
		
		if(frame == Frames.HIGHSCORES)
		{
			m_HighscoreFrame.setResultAndPopulateScoreBoard();
			this.setDisplayedFrame(m_HighscoreFrame);
		}
		
		if(frame == Frames.MAIN_MENU)
		{
			this.setDisplayedFrame(m_MainMenuFrame);
		}
		
		if(frame == Frames.OPTIONS)
		{
			m_OptionsFrame.setOptionsCheckers();
			this.setDisplayedFrame(m_OptionsFrame);
		}
		
		if(frame == Frames.INSTRUCTIONS)
		{
			this.setDisplayedFrame(m_InstructionsFrame);
		}
		
		if(frame == Frames.CREDITS)
		{
			this.setDisplayedFrame(m_CreditsFrame);
		}
		
		if(frame == Frames.DIFFICULTY)
		{
			this.setDisplayedFrame(m_DifficultyFrame);
		}
		
		if(frame == Frames.PLAY)
		{
			this.setDisplayedFrame(m_GameFrame);
			
			m_EntityManager.tearDown();
			m_BlockManager.tearDown();
			m_ScoreManager.tearDown();
			
			m_BlockManager = new BlockManager();
			
			m_EntityManager.initialize();
			m_ScoreManager.initialize();
			m_BlockManager.initialize();
			
			m_Loop = new Loop(m_BlockManager);
			m_Loop.setOwner(this);
			m_Loop.start();
		}
		
		if(frame == Frames.ENTER_NAME)
		{
			m_NameEntryFrame.setScoreAndLevel();
			this.setDisplayedFrame(m_NameEntryFrame);
		}
	}
	
	private void setDisplayedFrame(BasicFrame frameToDisplay)
	{
		m_RenderManager.cls();
		m_RenderManager.update(0);
		m_Panel.removeAll();
		Iterator<BasicFrame> it = m_Frames.iterator();
		
		while(it.hasNext())
		{
			BasicFrame panel = it.next();
			
			if(panel != frameToDisplay)
			{
				panel.displayFrame(false);
			}
		}
		
		frameToDisplay.displayFrame(true);
		m_Panel.add(frameToDisplay);
		m_Panel.doLayout();
		m_Panel.validate();
		this.doLayout();
		this.validate();
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	/*
	public void mouseClicked(MouseEvent arg0)
	{
	}
	*/
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	/*
	public void mouseEntered(MouseEvent arg0)
	{
		//the mouse has entered the applet
		//if the game is not playing, tell the user to play the game
	}
	*/
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	/*
	public void mouseExited(MouseEvent arg0)
	{
	}
	*/
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	/*
	public void mousePressed(MouseEvent arg0)
	{
	}
	*/
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	/*
	public void mouseReleased(MouseEvent arg0)
	{
	}
	*/
}
